<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>大文件上传下载</title>
  <link rel="stylesheet" type="text/css" href="../css/common/element-ui.css">
  <script src="../js/common/vue2.js"></script>
  <script src="../js/common/element-ui.js"></script>
  <script src="../js/common/axios.min.js"></script>
  <script src="../js/common/vue-resource.min.js"></script>
  <style>
    .fileList {
      width: 400px;
    }

    .downloadList {
      width: 620px;
    }

    .downloading {
      margin-top: 10px;
    }

    .downloading .fileName {
      margin-left: 76px;
      margin-right: 30px;
    }

    .downloading .fileSize {
      margin-right: 30px;
    }

    .downloading .progress {
      display: flex;
    }

    .progress .el-progress {
      width: 440px;
    }
  </style>

<body>
<div id="app">
  <div class="downloadList">
    <el-divider content-position="left">分片下载</el-divider>
    <el-input v-model="file.filePath" style="width: 527px;" placeholder="文件路径"></el-input>
    <el-button type="primary" @click="downloadFile(file)">下载</el-button>

    <div class="downloading">
      <span class="fileName">{{ file.name }}</span>
      <span class="fileSize">{{ formatSize(file.size) }}</span>

      <div class="progress">
        <span>进度：</span>
        <el-progress :text-inside="true" :stroke-width="18" :percentage="file.progress"></el-progress>
        <span style="margin-left: 10px;" class="downloadSpeed">{{ file.downloadSpeed }}</span>
      </div>
    </div>
  </div>

  <div class="downloadList">
    <el-divider content-position="left">分片上传</el-divider>
    <input type="file" @change="selectFile">
    <button @click="upload">上传</button>
  </div>

</div>
</body>
<script>
  var vue = new Vue({
    el: '#app',
    data: function () {
      return {
        sepSize: 5,
        file: {
          filePath: 'D:\\tmp\\fc14c3ce710ce7d56dc44409cf946685.zip',
          size: 0,
          name: '',
          progress: 0,
          downloadSpeed: "0 M/s"
        },
        upFile: {
          file: null,
          currentChunk: 0,  // 当前分片
          totalChunk: 0, // 总分片数
          fileName: ''   // 文件名
        }
      }
    },
    methods: {
      // 显示消息
      showMsg(msg, msgType) {
        this.$message({ showClose: true, message: msg, type: msgType });
      },
      // 点击下载文件
      downloadFile(file) {
        // 请求文件基础信息
        axios({
          url: "http://localhost:8000/upms/file", method: "get", params: { "filePath": file.filePath }, timeout: 50000
        }).then((res) => {
          if (res.data.code == 0) {
            file.size = res.data.data.fileSize;
            file.md5 = res.data.data.md5;
            file.name = res.data.data.fileName;
            file.blobList = [];
            file.chunkList = [];

            this.downloadChunk(1, file);
          } else {
            this.showMsg(res.data.msg, 'warning');
          }
        });
      },
      // 点击下载文件分片
      downloadChunk(currentChunk, file) {
        var chunkSize = 1024 * 1024 * this.sepSize;
        var chunkTotal = Math.ceil(file.size / chunkSize);

        if (currentChunk <= chunkTotal) {
          var exit = file.chunkList.includes(currentChunk);
          if (!exit) {
            // 构建参数
            var formData = new FormData();
            formData.append("fileName", file.name);
            formData.append("filePath", file.filePath)
            formData.append("chunkSize", chunkSize);
            formData.append("currentChunk", currentChunk);
            formData.append("totalChunk", chunkTotal);

            if (currentChunk * chunkSize >= file.size) {
              chunkSize = file.size - (currentChunk - 1) * chunkSize;
              formData.set("chunkSize", chunkSize);
            }

            var startTime = new Date().valueOf();

            axios({
              url: "http://localhost:8000/upms/file/splitDownload", method: "post", data: formData, responseType: "blob", timeout: 50000
            }).then((res) => {
              file.chunkList.push(currentChunk);
              file.blobList.push(res.data);
              // 下载速度
              var timeDif = (new Date().valueOf() - startTime) / 1000;
              file.downloadSpeed = (5 / timeDif).toFixed(1) + " M/s";
              // 下载进度
              file.progress = parseInt((currentChunk / chunkTotal) * 100);

              // 如果当前分片数等于总分片数，合并文件并下载
              if (currentChunk == chunkTotal) {
                this.mergeFiles(currentChunk, chunkTotal, file);
              }

              this.downloadChunk(currentChunk + 1, file);
            });
          } else {
            file.downloadPersentage = parseInt((index / chunkTotal) * 100);
            downloadChunk(index + 1, file);
          }
        }
      },
      // 格式化文件大小
      formatSize(size) {
        var unit;
        var units = [" B", " K", " M", " G"];
        while ((unit = units.shift()) && size > 1024) {
          size = size / 1024;
        }
        return (unit === "B" ? size : size.toFixed(2)) + unit;
      },
      // 合并文件并下载
      mergeFiles(currentChunk, chunkTotal, file) {
        let url = window.URL.createObjectURL(
          new Blob(file.blobList, { type: "application/octet-stream" })
        ); // 将获取的文件转化为blob格式
        let a = document.createElement("a"); // 此处向下是打开一个储存位置
        a.style.display = "none";
        a.href = url;
        var fileName = file.name;

        a.setAttribute("download", fileName);
        document.body.appendChild(a);
        a.click(); //点击下载
        document.body.removeChild(a); // 下载完成移除元素
        window.URL.revokeObjectURL(url); // 释放掉blob对象

        this.showMsg('分片文件下载成功，正在合并，请稍等...', 'success');
      },


      // 可以根据后端配置的上传文件大小进行判断是否需要分片
      selectFile(e) {
        this.upFile.file = e.target.files[0]
        this.upFile.fileName = this.upFile.file.name
        // 分片大小根据实际需要 调整 可以配置在前端配置文件中
        this.upFile.totalChunk = Math.ceil(this.upFile.file.size / 1024 / 1024 / this.sepSize) // 2MB一片
      },
      upload() {
        // 现将文件分片
        const formData = new FormData()
        formData.append('file', this.upFile.file.slice(this.upFile.currentChunk * 1024 * 1024 * this.sepSize, (this.upFile.currentChunk + 1) * 1024 * 1024 * this.sepSize))
        formData.append('currentChunk', this.upFile.currentChunk)
        formData.append('totalChunk', this.upFile.totalChunk)
        formData.append('fileName', this.upFile.fileName)
        // 然后递归调用文件上传接口
        axios.post('http://localhost:8092/file/splitUpload', formData)
          .then(() => {
            this.upFile.currentChunk++
            if (this.upFile.currentChunk < this.upFile.totalChunk) {
              this.upload()
            }
          })
      }
    }
  });


</script>

</html>